# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug
# Copyright: (c) <spug.dev@gmail.com>
# Released under the AGPL-3.0 License.
from django.views.generic import View
from django.db.models import F
from django.http.response import HttpResponseBadRequest
from libs import json_response, JsonParser, Argument
from apps.setting.utils import AppSetting
from apps.idc.models import Idc
from apps.host.models import Host
from libs.tencent_api import TencentCvmApi
from apps.app.models import Deploy
from apps.schedule.models import Task
from apps.monitor.models import Detection
from apps.account.models import Role
from libs.ssh import SSH, AuthenticationException
from paramiko.ssh_exception import BadAuthenticationType
from libs import human_datetime, AttrDict
from openpyxl import load_workbook
import socket


class IdcView(View):
    def get(self, request):
        idc_id = request.GET.get('id')
        if idc_id:
            if not request.user.has_host_perm(idc_id):
                return json_response(error='无权访问该主机，请联系管理员')
            return json_response(Idc.objects.get(pk=idc_id))
        idcs = Idc.objects.filter(deleted_by_id__isnull=True)

        return json_response(idcs)

    def post(self, request):
        secret_required = False
        type, error = JsonParser(
            Argument('type', handler=str.strip, help='请输入IDC类型'),
        ).parse(request.body)
        if type.type is None:
            pass
        else:
            if type.type == 'tencent':
                secret_required = True
        form, error = JsonParser(
            Argument('id', type=int, required=False),
            Argument('key', help='请输入唯一标识符'),
            Argument('name', help='请输IDC名称'),
            Argument('type', handler=str.strip, help='请输入IDC类型'),
            Argument('secretid', required=secret_required, handler=str.strip, help='请输入密钥ID'),
            Argument('secretkey', required=secret_required, handler=str.strip, help='请输入密钥密码'),
            Argument('desc', required=False),
        ).parse(request.body)
        tencent_cvm_client = TencentCvmApi(form.secretid, form.secretkey)
        try:
            instances = tencent_cvm_client.get_all()
        except:
            return json_response(error=f'密钥ID或密钥密码不正确，请修改。')
        else:
            pass
        if error is None:
            if form.id:
                # 如果传的参数带id,则更新IDC
                Idc.objects.filter(pk=form.pop('id')).update(**form)
            elif Idc.objects.filter(name=form.name, deleted_by_id__isnull=True).exists():
                # IDC名称唯一，如果已存在相同的IDC名称则报错
                return json_response(error=f'已存在的IDC名称【{form.name}】')
            else:
                # 新建IDC记录
                host = Idc.objects.create(created_by=request.user, **form)
                if request.user.role:
                    request.user.role.add_host_perm(host.id)
        return json_response(error=error)

    def delete(self, request):
        form, error = JsonParser(
            Argument('id', type=int, help='请指定操作对象')
        ).parse(request.GET)
        if error is None:
            role = Role.objects.filter(host_perms__regex=fr'[^0-9]{form.id}[^0-9]').first()
            host = Host.objects.filter(idc_id=form.id).first()
            if role:
                return json_response(error=f'角色【{role.name}】的主机权限关联了该主机，请解除关联后再尝试删除该主机')
            if host:
                return json_response(error=f'主机【{host.name}】关联了该IDC，请解除关联后再尝试删除该IDC')
            Idc.objects.filter(pk=form.id).update(
                deleted_at=human_datetime(),
                deleted_by=request.user,
            )
        return json_response(error=error)


def post_import(request):
    password = request.POST.get('password')
    file = request.FILES['file']
    ws = load_workbook(file, read_only=True)['Sheet1']
    summary = {'invalid': [], 'skip': [], 'fail': [], 'network': [], 'repeat': [], 'success': [], 'error': []}
    for i, row in enumerate(ws.rows):
        if i == 0:  # 第1行是表头 略过
            continue
        if not all([row[x].value for x in range(5)]):
            summary['invalid'].append(i)
            continue
        data = AttrDict(
            zone=row[0].value,
            name=row[1].value,
            hostname=row[2].value,
            port=row[3].value,
            username=row[4].value,
            password=row[5].value,
            desc=row[6].value
        )
        if Host.objects.filter(hostname=data.hostname, port=data.port, username=data.username,
                               deleted_by_id__isnull=True).exists():
            summary['skip'].append(i)
            continue
        try:
            if valid_ssh(data.hostname, data.port, data.username, data.pop('password') or password, None,
                         False) is False:
                summary['fail'].append(i)
                continue
        except AuthenticationException:
            summary['fail'].append(i)
            continue
        except socket.error:
            summary['network'].append(i)
            continue
        except Exception:
            summary['error'].append(i)
            continue
        if Host.objects.filter(name=data.name, deleted_by_id__isnull=True).exists():
            summary['repeat'].append(i)
            continue
        host = Host.objects.create(created_by=request.user, **data)
        if request.user.role:
            request.user.role.add_host_perm(host.id)
        summary['success'].append(i)
    return json_response(summary)


def valid_ssh(hostname, port, username, password=None, pkey=None, with_expect=True):
    try:
        private_key = AppSetting.get('private_key')
        public_key = AppSetting.get('public_key')
    except KeyError:
        private_key, public_key = SSH.generate_key()
        AppSetting.set('private_key', private_key, 'ssh private key')
        AppSetting.set('public_key', public_key, 'ssh public key')
    if password:
        _cli = SSH(hostname, port, username, password=str(password))
        _cli.add_public_key(public_key)
    if pkey:
        private_key = pkey
    try:
        cli = SSH(hostname, port, username, private_key)
        cli.ping()
    except BadAuthenticationType:
        if with_expect:
            raise TypeError('该主机不支持密钥认证，请参考官方文档，错误代码：E01')
        return False
    except AuthenticationException:
        if password and with_expect:
            raise ValueError('密钥认证失败，请参考官方文档，错误代码：E02')
        return False
    return True


def post_parse(request):
    file = request.FILES['file']
    if file:
        data = file.read()
        return json_response(data.decode())
    else:
        return HttpResponseBadRequest()
